#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
项目自动部署脚本
用于将项目从开发环境部署到生产环境
"""

import os
import sys
import time
import shutil
import argparse
import subprocess
import configparser
from pathlib import Path

# 默认配置
DEFAULT_CONFIG = {
    'source_dir': './project',
    'dest_dir': '/opt/deployment/project',
    'backup_dir': '/opt/backups/project',
    'requirements': 'requirements.txt',
    'services': 'gunicorn,celery',
    'user': 'www-data',
    'group': 'www-data',
    'python_path': '/usr/bin/python3',
    'venv_path': '/opt/venvs/project',
    'pre_deploy_commands': '',
    'post_deploy_commands': '',
    'supervisor_config': '/etc/supervisor/conf.d/project.conf',
    'create_backup': 'true',
    'migrate_db': 'true',
    'collect_static': 'true',
    'branch': 'master'
}

def run_command(command, cwd=None, shell=True):
    """执行命令并返回结果"""
    print(f"执行命令: {command}")
    try:
        result = subprocess.run(
            command,
            shell=shell,
            check=True,
            cwd=cwd,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True
        )
        print(f"命令执行成功")
        return result.stdout
    except subprocess.CalledProcessError as e:
        print(f"命令执行失败: {e}")
        print(f"错误输出: {e.stderr}")
        raise

def create_backup(src_dir, backup_dir, config):
    """创建备份"""
    if config.get('create_backup', 'true').lower() != 'true':
        print("跳过备份过程")
        return
    
    print(f"创建备份: {src_dir} -> {backup_dir}")
    timestamp = time.strftime('%Y%m%d_%H%M%S')
    backup_path = os.path.join(backup_dir, f"backup_{timestamp}")
    
    try:
        # 确保备份目录存在
        os.makedirs(backup_dir, exist_ok=True)
        
        # 如果源目录已存在，则创建备份
        if os.path.exists(src_dir):
            shutil.copytree(src_dir, backup_path)
            print(f"备份已创建: {backup_path}")
            return backup_path
        else:
            print(f"源目录不存在，跳过备份: {src_dir}")
            return None
    except Exception as e:
        print(f"创建备份失败: {e}")
        raise

def setup_virtualenv(venv_path, requirements, python_path):
    """设置Anaconda虚拟环境并安装依赖"""
    print(f"使用Anaconda环境: {venv_path}")
    
    # 不需要创建虚拟环境，直接使用现有的Anaconda环境
    
    # 安装依赖
    if os.path.exists(requirements):
        print(f"安装依赖: {requirements}")
        conda_bin = os.path.join(os.path.dirname(python_path), 'conda')
        if os.path.exists(conda_bin):
            # 尝试使用conda安装
            try:
                run_command(f"{conda_bin} install -y --file {requirements}")
            except:
                # 如果conda安装失败，使用pip安装
                pip_path = os.path.join(venv_path, 'bin', 'pip')
                run_command(f"{pip_path} install -r {requirements}")
        else:
            # 如果找不到conda，使用pip安装
            pip_path = os.path.join(venv_path, 'bin', 'pip')
            run_command(f"{pip_path} install -r {requirements}")
    else:
        print(f"依赖文件不存在，跳过安装: {requirements}")

def django_tasks(venv_path, project_dir, config):
    """执行Django相关任务"""
    python_bin = os.path.join(venv_path, 'bin', 'python')
    
    # 数据库迁移
    if config.get('migrate_db', 'true').lower() == 'true':
        print("执行数据库迁移")
        run_command(f"{python_bin} manage.py migrate", cwd=project_dir)
    
    # 收集静态文件
    if config.get('collect_static', 'true').lower() == 'true':
        print("收集静态文件")
        run_command(f"{python_bin} manage.py collectstatic --noinput", cwd=project_dir)

def update_services(services, config):
    """更新服务配置并重启服务"""
    if not services:
        print("没有指定服务，跳过服务更新")
        return
    
    print(f"更新服务: {services}")
    
    # 更新supervisor配置（如果需要）
    supervisor_config = config.get('supervisor_config')
    if supervisor_config and os.path.exists(supervisor_config):
        print("更新supervisor配置")
        run_command("supervisorctl reread")
        run_command("supervisorctl update")
    
    # 重启服务
    service_list = services.split(',')
    for service in service_list:
        service = service.strip()
        if service:
            print(f"重启服务: {service}")
            try:
                # 首先尝试使用systemd
                run_command(f"systemctl restart {service}")
            except:
                try:
                    # 如果失败，尝试使用supervisor
                    run_command(f"supervisorctl restart {service}")
                except:
                    print(f"无法重启服务: {service}, 请手动重启")

def deploy_code(source_dir, dest_dir, config):
    """部署代码"""
    print(f"部署代码: {source_dir} -> {dest_dir}")
    
    # 确保目标目录存在
    os.makedirs(os.path.dirname(dest_dir), exist_ok=True)
    
    # 如果目标目录已存在，删除它
    if os.path.exists(dest_dir):
        print(f"删除现有目标目录: {dest_dir}")
        shutil.rmtree(dest_dir)
    
    # 复制代码
    print(f"复制代码")
    shutil.copytree(source_dir, dest_dir)
    
    # 设置权限
    user = config.get('user')
    group = config.get('group')
    if user and group:
        print(f"设置权限: {user}:{group}")
        run_command(f"chown -R {user}:{group} {dest_dir}")

def pull_from_git(repo_dir, branch):
    """从Git拉取最新代码"""
    if not os.path.exists(os.path.join(repo_dir, '.git')):
        print(f"目录不是Git仓库: {repo_dir}")
        return False
    
    print(f"从Git拉取最新代码，分支: {branch}")
    run_command(f"git fetch", cwd=repo_dir)
    run_command(f"git checkout {branch}", cwd=repo_dir)
    run_command(f"git pull origin {branch}", cwd=repo_dir)
    return True

def run_custom_commands(commands, cwd=None):
    """运行自定义命令"""
    if not commands:
        return
    
    print(f"执行自定义命令:")
    command_list = commands.split(';')
    for cmd in command_list:
        cmd = cmd.strip()
        if cmd:
            print(f"执行: {cmd}")
            run_command(cmd, cwd=cwd)

def main():
    parser = argparse.ArgumentParser(description='项目自动部署脚本')
    parser.add_argument('--config', help='配置文件路径')
    parser.add_argument('--source', help='源目录')
    parser.add_argument('--dest', help='目标目录')
    parser.add_argument('--venv', help='虚拟环境路径')
    parser.add_argument('--branch', help='Git分支名称')
    parser.add_argument('--no-backup', action='store_true', help='跳过备份')
    parser.add_argument('--no-migrate', action='store_true', help='跳过数据库迁移')
    parser.add_argument('--no-static', action='store_true', help='跳过静态文件收集')
    
    args = parser.parse_args()
    
    config = DEFAULT_CONFIG.copy()
    
    # 读取配置文件
    if args.config and os.path.exists(args.config):
        print(f"读取配置文件: {args.config}")
        conf = configparser.ConfigParser()
        conf.read(args.config)
        if 'deploy' in conf:
            for key, value in conf['deploy'].items():
                config[key] = value
    
    # 命令行参数覆盖配置文件
    if args.source:
        config['source_dir'] = args.source
    if args.dest:
        config['dest_dir'] = args.dest
    if args.venv:
        config['venv_path'] = args.venv
    if args.branch:
        config['branch'] = args.branch
    if args.no_backup:
        config['create_backup'] = 'false'
    if args.no_migrate:
        config['migrate_db'] = 'false'
    if args.no_static:
        config['collect_static'] = 'false'
    
    # 确保各目录路径是绝对路径
    config['source_dir'] = os.path.abspath(config['source_dir'])
    config['dest_dir'] = os.path.abspath(config['dest_dir'])
    config['backup_dir'] = os.path.abspath(config['backup_dir'])
    config['venv_path'] = os.path.abspath(config['venv_path'])
    
    try:
        print("开始部署过程...")
        
        # 如果源目录是Git仓库，拉取最新代码
        pull_from_git(config['source_dir'], config['branch'])
        
        # 执行部署前命令
        run_custom_commands(config.get('pre_deploy_commands'), cwd=config['source_dir'])
        
        # 创建备份
        backup_path = create_backup(config['dest_dir'], config['backup_dir'], config)
        
        # 设置虚拟环境
        setup_virtualenv(
            config['venv_path'], 
            os.path.join(config['source_dir'], config['requirements']),
            config['python_path']
        )
        
        # 部署代码
        deploy_code(config['source_dir'], config['dest_dir'], config)
        
        # 执行Django任务
        django_tasks(config['venv_path'], config['dest_dir'], config)
        
        # 执行部署后命令
        run_custom_commands(config.get('post_deploy_commands'), cwd=config['dest_dir'])
        
        # 更新服务
        update_services(config.get('services'), config)
        
        print("部署完成!")
        print(f"代码已部署到: {config['dest_dir']}")
        if backup_path:
            print(f"备份路径: {backup_path}")
        
        return 0
        
    except Exception as e:
        print(f"部署失败: {e}")
        return 1

if __name__ == '__main__':
    sys.exit(main()) 